// Mechanicoder
// 2022/11/13

#include "view_window.h"
#include "View.h"
#include "ui_ctrl_widget.h"

#include <BRepLib_PointCloudShape.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeTorus.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <STEPControl_Reader.hxx>
#include <BRepLib_PointCloudShape.hxx>
#include <Graphic3d_ArrayOfPoints.hxx>
#include <AIS_Shape.hxx>
#include <AIS_PointCloud.hxx>
#include <AIS_InteractiveObject.hxx>
#include <AIS_ViewCube.hxx>
#include <AIS_Trihedron.hxx>
#include <AIS_MediaPlayer.hxx>
#include <Prs3d_PointAspect.hxx>
#include <Prs3d_Drawer.hxx>
#include <Graphic3d_AspectFillArea3d.hxx>
#include <Geom_Axis2Placement.hxx>

#include <QHBoxLayout>
#include <QFileDialog>
#include <QDebug>
#include <QLineEdit>
#include <QSplitter>

// 图层 Context 类型
enum ContextType
{
    CT_Topo_Box = 0,
    CT_Topo_Torus,
    CT_AIS_Trihedron,
    CT_Topo_Sphere,
    CT_AIS_PointCloud,

    CT_Num
};

ViewWindow::ViewWindow(QWidget* parent)
    : QMainWindow(parent)
{
    Handle(V3d_Viewer) viewer = ViewHelper::InitViewer();

    _contexts.resize(CT_Num);
    for (size_t i = 0; i < _contexts.size(); i++)
    {
        _contexts[i] = ViewHelper::InitContext(viewer);
    }

    _view = new View(_contexts, nullptr); // 以第一个初始化
    this->setCentralWidget(_view);

    InitCtrlWidget();

    this->resize(800, 600);

    std::time_t t = std::time(nullptr);
    std::srand(t);
}

ViewWindow::~ViewWindow()
{
}

void ViewWindow::OnHideShape()
{
    QPushButton* sender_button = (QPushButton*)sender();
    int type = 0;
    for (; type < CT_Num; ++type)
    {
        if (sender_button == _hideButtons[type])
        {
            break;
        }
    }

    if (type >= CT_Num)
    {
        return;
    }

    Handle(AIS_InteractiveContext) curr_context = _contexts[type];

    AIS_ListOfInteractive objs;
    curr_context->ErasedObjects(objs);
    if (objs.IsEmpty())
    {
        curr_context->EraseAll(true);
    }
    else
    {
        curr_context->DisplayAll(true);
    }
}

// 0-topo box, 1-topo tours, 3- ais_trihedron, 4-ais triangulation, 5-pyramid
void ViewWindow::OnRandomGeneration()
{
    ContextType type = CurrentContextType();

    Handle(AIS_InteractiveContext) curr_context = GetCurrentContext(type);

    switch (type)
    {
    case CT_Topo_Box: GenerateTopoBox(curr_context); break;
    case CT_Topo_Torus: GenerateTopoTorus(curr_context); break;
    case CT_AIS_Trihedron: GenerateAISThihedron(curr_context); break;
    case CT_Topo_Sphere: GenerateTopoSphere(curr_context); break;
    case CT_AIS_PointCloud: GenerateAISPointCloud(curr_context); break;
    default: break;
    }

    AIS_ListOfInteractive objs;
    curr_context->DisplayedObjects(objs);
    int num = objs.Size();

    if (type < _hideButtons.size())
    {
        _hideButtons[type]->setText(MakeHideButtonText(type, num));
    }

    _view->update();
}

void ViewWindow::InitCtrlWidget()
{
    QWidget* widget = new QWidget(_view);

    _ctrlUi = new Ui::CtrlWidgetUi;
    _ctrlUi->setupUi(widget);
    widget->setAutoFillBackground(true);

    QVBoxLayout* vlayout = new QVBoxLayout();
    vlayout->addWidget(widget);
    vlayout->addStretch();

    QHBoxLayout* hlayout = new QHBoxLayout(_view);
    hlayout->addLayout(vlayout);
    hlayout->addStretch();

    connect(_ctrlUi->pushButton_randomGeneration, &QPushButton::clicked, this, &ViewWindow::OnRandomGeneration);

    _hideButtons.resize(CT_Num);
    for (int i = 0; i < CT_Num; i++)
    {
        ContextType type = ContextType(i);

        _hideButtons[i] = new QPushButton(MakeHideButtonText(type, 0), widget);
        vlayout->addWidget(_hideButtons[i]);

        connect(_hideButtons[i], &QPushButton::clicked, this, &ViewWindow::OnHideShape);
    }
}

void ViewWindow::GenerateTopoBox(const Handle(AIS_InteractiveContext)& curr_context)
{
    std::vector<double> v= GenRandomValues(6);
    gp_Pnt corner(v[0], v[1], v[2]);
    TopoDS_Shape box = BRepPrimAPI_MakeBox(corner, std::abs(v[3]), std::abs(v[4]), std::abs(v[5]));
    DisplayShape(curr_context, box);

    //Handle(AIS_MediaPlayer) ais = new AIS_MediaPlayer();
    //ais->OpenInput("D:/1.mp4", true);
    //curr_context->Display(ais, true);
    //ais->PresentFrame(Graphic3d_Vec2i(300, 300), Graphic3d_Vec2i(100, 100));
    //ais->PlayPause();
}

void ViewWindow::GenerateTopoTorus(const Handle(AIS_InteractiveContext)& curr_context)
{
    gp_Pnt center = GenRandomXYZ();
    gp_Dir zdir = GenRandomDir();

    gp_Ax2 ax2(center, zdir);

    std::vector<double> rs = GenRandomValues(2);
    rs[1] = int(rs[1]) % 30;
    TopoDS_Shape torus = BRepPrimAPI_MakeTorus(ax2, std::abs(rs[0]), std::abs(rs[1]));
    DisplayShape(curr_context, torus);
}

void ViewWindow::GenerateAISThihedron(const Handle(AIS_InteractiveContext)& curr_context)
{
    gp_Pnt center = GenRandomXYZ();
    gp_Dir zdir = GenRandomDir();
    gp_Dir xdir = GenRandomDir();

    Handle(Geom_Axis2Placement) placement = new Geom_Axis2Placement(center, zdir, xdir);
    Handle(AIS_Trihedron) trihedron = new AIS_Trihedron(placement);
    trihedron->SetXAxisColor(Quantity_NOC_RED);
    trihedron->SetYAxisColor(Quantity_NOC_GREEN);
    trihedron->SetWidth(2);
    curr_context->Display(trihedron, AIS_DisplayMode::AIS_Shaded, -1, true);
    curr_context->CurrentViewer()->Redraw();
}

void ViewWindow::GenerateTopoSphere(const Handle(AIS_InteractiveContext)& curr_context)
{
    gp_Pnt center = GenRandomXYZ();
    double r = GetRandomValue();
    TopoDS_Shape sphere = BRepPrimAPI_MakeSphere(center, std::abs(r));
    DisplayShape(curr_context, sphere);
}

void ViewWindow::GenerateAISPointCloud(const Handle(AIS_InteractiveContext)& curr_context)
{
    int num = GetRandomValue();
    if (num < 2)
    {
        return;
    }

    Graphic3d_ArrayFlags flag = Graphic3d_ArrayFlags_VertexNormal
        | Graphic3d_ArrayFlags_VertexColor | Graphic3d_ArrayFlags_VertexTexel;

    Handle(Graphic3d_ArrayOfPoints) points = new Graphic3d_ArrayOfPoints(num, flag);
    for (int i = 1; i <= num; i++)
    {
        points->SetVertice(i, GenRandomXYZ());
    }
    // set array of points in point cloud object
    Handle(AIS_PointCloud) pnt_cloud = new AIS_PointCloud();
    const Handle(Prs3d_Drawer)& drawer = pnt_cloud->Attributes();
    drawer->ShadingAspect()->Aspect()->SetMarkerType(Aspect_TOM_O_PLUS);
    pnt_cloud->SetPoints(points);
    curr_context->Display(pnt_cloud, true);
}

Handle(AIS_InteractiveContext) ViewWindow::GetCurrentContext(int type/* = -1*/) const
{
    ContextType context_type =  (type >= 0 && type < CT_Num) ? (ContextType)(type) : CurrentContextType();
    return _contexts[int(context_type % CT_Num)];
}

ContextType ViewWindow::CurrentContextType() const
{
    int type = std::rand();

    return (ContextType)(type % CT_Num);
}

QString ViewWindow::MakeHideButtonText(ContextType type, int obj_num) const
{
    QString text = QString("S/H %1-Objs[%2]").arg(TypeToString(type)).arg(obj_num);
    return text;
}

QString ViewWindow::TypeToString(ContextType type) const
{
    switch (type)
    {
    case CT_Topo_Box: return "Topo_Box"; break;
    case CT_Topo_Torus: return "Topo_Torus"; break;
    case CT_AIS_Trihedron: return "AIS_Trihedron"; break;
    case CT_Topo_Sphere: return "Topo_Sphere"; break;
    case CT_AIS_PointCloud: return "AIS_PointCloud"; break;
    default: return "Unknown"; break;
    }
}

gp_Dir ViewWindow::GenRandomDir() const
{
    gp_Dir dir;
    do
    {
        gp_XYZ xyz = GenRandomXYZ();
        if (xyz.Modulus() > Precision::Confusion())
        {
            dir = xyz;
            break;
        }
    } while (true);

    return dir;
}

gp_XYZ ViewWindow::GenRandomXYZ() const
{
    std::vector<double> vs = GenRandomValues(3);
    return gp_XYZ(vs[0], vs[1], vs[2]);
}

std::vector<double> ViewWindow::GenRandomValues(int num) const
{
    std::vector<double> values(num);
    for (int i = 0; i < num; i++)
    {
        values[i] = GetRandomValue();
    }
    return values;
}

double ViewWindow::GetRandomValue() const
{
    int value = std::rand();

    const int length = 2000;
    const int half_len = 1000;
    int round = value % length;
    int sign = round > half_len ? 1 : -1;

    return sign * (round % half_len);
}

void ViewWindow::DisplayShape(const Handle(AIS_InteractiveContext)& curr_contex,
    const TopoDS_Shape& shape) const
{
    Handle(AIS_Shape) ais = new AIS_Shape(shape);
    ais->SetTransparency();
    curr_contex->Display(ais, true);
}
